package com.topc.code.judge.template.language.cpp;

import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.WordTree;
import com.topc.code.judge.CodeSandbox;
import com.topc.code.judge.template.CommonCodeSandboxTemplate;
import com.topc.code.utils.ProcessUtils;
import com.topc.problem.model.code.ExecuteCodeRequest;
import com.topc.problem.model.code.ExecuteCodeResponse;
import com.topc.problem.model.code.ExecuteMessage;
import com.topc.problem.model.code.JudgeInfo;
import com.topc.problem.model.enums.JudgeInfoMessageEnum;
import com.topc.problem.model.enums.QuestionSubmitStatusEnum;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class CppNativeCodeSandbox extends CommonCodeSandboxTemplate implements CodeSandbox {
    private static final String GLOBAL_CODE_DIR_NAME="cppCode";
    private static final String GLOBAL_CPP_CLASS_NAME ="Main.cpp";

    private static final long TIME_OUT = 5000L;

    private static final List<String> blackList= Arrays.asList("Files","exec");
    private static final WordTree WORD_TREE;

    static
    {
        // 初始化黑名单字典树
        WORD_TREE = new WordTree();
        WORD_TREE.addWords(blackList);
    }
    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest)
    {
        List<String> inputList = executeCodeRequest.getInputList();
        String code = executeCodeRequest.getCode();
        String language = executeCodeRequest.getLanguage();
        System.out.println("当前操作系统：" + System.getProperty("os.name").toLowerCase());
        System.out.println("当前代码使用语言：" + language);
//        List<ExecuteCodeResponse> executeCodeResponses = new ArrayList<>();
        // 0. 安全控制：限制敏感代码：黑名单检测
        FoundWord foundWord = WORD_TREE.matchWord(code);
        if (foundWord != null)
        {
            System.out.println("包含禁止词：" + foundWord.getFoundWord());
            // 返回错误信息
            ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse(null,null,null, "包含禁止词：" + foundWord.getFoundWord(), QuestionSubmitStatusEnum.FAILED.getValue(), new JudgeInfo(JudgeInfoMessageEnum.DANGEROUS_OPERATION.getValue(), null, null));
//            executeCodeResponses.add(executeCodeResponse);
            return executeCodeResponse;
        }

        // 1. 把用户的代码保存为文件
        File userCodeFile = saveCodeToFile(code, GLOBAL_CODE_DIR_NAME, GLOBAL_CPP_CLASS_NAME);

        // 2. 编译代码，得到 class 文件
        ExecuteMessage compileFileExecuteMessage = compileFile(userCodeFile);
        System.out.println("编译结果：" + compileFileExecuteMessage);
        List<ExecuteMessage> executeMessageList=new ArrayList<>();
        ExecuteCodeResponse outputResponse=new ExecuteCodeResponse();
        if (compileFileExecuteMessage.getErrorMessage() != null)
        {
            BigDecimal errorNum= BigDecimal.valueOf(0);
            // 返回编译错误信息
            outputResponse = new ExecuteCodeResponse(null,null,null, compileFileExecuteMessage.getMessage(), QuestionSubmitStatusEnum.FAILED.getValue(), new JudgeInfo(compileFileExecuteMessage.getMessage(), errorNum, errorNum));
        }else {
            // 3. 执行代码，得到输出结果
            executeMessageList = runFile(userCodeFile, inputList);
            System.out.println(executeMessageList);
            // 4. 收集整理输出结果
            outputResponse = getOutputResponse(executeMessageList);
        }
        // 5. 文件清理
        boolean b = deleteFile(userCodeFile);
        if (!b)
        {
            log.error("deleteFile error, userCodeFilePath = {}", userCodeFile.getAbsolutePath());
        }

        return outputResponse;
    }

    /**
     * 编译代码
     *
     * @param userCodeFile
     * @return
     */
    public ExecuteMessage compileFile(File userCodeFile)
    {
        String userCodeParentPath = userCodeFile.getParentFile().getAbsolutePath();
        String compileCmd = String.format("g++ -o %s %s", userCodeParentPath+"/Main", userCodeFile.getAbsolutePath());
        try
        {
            Process compileProcess = Runtime.getRuntime().exec(compileCmd);
            ExecuteMessage executeMessage = ProcessUtils.compileProcessAndGetMessage(compileProcess, "编译");
//            executeMessage.setMessage(JudgeInfoMessageEnum.WAITING.getText());
            // 编译失败
            if (executeMessage.getExitValue() != 0)
            {
                executeMessage.setExitValue(1);
                executeMessage.setMessage(JudgeInfoMessageEnum.COMPILE_ERROR.getText());
                executeMessage.setErrorMessage(JudgeInfoMessageEnum.COMPILE_ERROR.getValue());
            }
            return executeMessage;
        }
        catch (Exception e)
        {
            // 未知错误
            ExecuteMessage executeMessage = new ExecuteMessage();
            executeMessage.setExitValue(1);
            executeMessage.setMessage(e.getMessage());
            executeMessage.setErrorMessage(JudgeInfoMessageEnum.SYSTEM_ERROR.getValue());
            return executeMessage;
        }
    }

    /**
     * 执行文件，获得执行结果列表
     *
     * @param userCodeFile
     * @param inputList
     * @return
     */
    public List<ExecuteMessage> runFile(File userCodeFile, List<String> inputList)
    {
        String userCodeParentPath = userCodeFile.getParentFile().getAbsolutePath();
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for (String inputArgs : inputList)
        {
            // 安全控制：限制资源分配：最大队资源大小：128MB
//            String runCmd = String.format("java -Xmx128m -Dfile.encoding=UTF-8 -cp %s;%s -Djava.security.manager=%s Main %s", userCodeParentPath, SECURITY_MANAGER_PATH, SECURITY_MANAGER_CLASS_NAME, inputArgs);
            String runCmd = String.format("%s", userCodeParentPath+"/Main");
            String osName = System.getProperty("os.name").toLowerCase();
            // 如果是Windows系统，支持安全管理器security-manager的创建，反之是Linux则不支持（可能也支持，但作者暂时因时间原因未找出对策，故出此下策）
            if (osName.contains("nix") || osName.contains("nux")) {
                runCmd = String.format("sudo %s", userCodeParentPath+"/Main");
            }
            try
            {
                Process runProcess = Runtime.getRuntime().exec(runCmd);
                // 安全控制：限制最大运行时间，超时控制
                new Thread(() ->
                {
                    try
                    {
                        Thread.sleep(TIME_OUT);
                        System.out.println("超过程序最大运行时间，终止进程");
                        runProcess.destroy();
                    }
                    catch (InterruptedException e)
                    {
                        throw new RuntimeException(e);
                    }
                }).start();
                ExecuteMessage executeMessage = ProcessUtils.runInteractProcessAndGetMessage(runProcess, "运行",inputArgs);
                System.out.println("本次运行结果：" + executeMessage);
                if (executeMessage.getExitValue() != 0)
                {
                    executeMessage.setExitValue(1);
                    executeMessage.setMessage(JudgeInfoMessageEnum.RUNTIME_ERROR.getText());
                    executeMessage.setErrorMessage(JudgeInfoMessageEnum.RUNTIME_ERROR.getValue());
                }
                executeMessageList.add(executeMessage);
            }
            catch (Exception e)
            {
                // 未知错误
                ExecuteMessage executeMessage = new ExecuteMessage();
                executeMessage.setExitValue(1);
                executeMessage.setMessage(e.getMessage());
                executeMessage.setErrorMessage(JudgeInfoMessageEnum.SYSTEM_ERROR.getValue());
                executeMessageList.add(executeMessage);
            }
        }
        return executeMessageList;
    }
}
